package com.servicerequestinfo.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.serviceinfo.model.ParameterInfo;
import com.serviceinfo.model.ServiceInfo;
import com.servicerequestinfo.model.ServiceRequestInfo;
import com.servicerequestinfo.model.ServiceRequestInfoBuilder;

public class ServiceRequestInfoImpl implements ServiceRequestInfo {

	private final ServiceInfo serviceInfo;

	private final String serviceUrl;

	private final Map<String, String> headers = new HashMap<String, String>();

	private final Map<ParameterInfo, List<Object>> queryValues = new HashMap<ParameterInfo, List<Object>>();

	private final Map<ParameterInfo, List<Object>> formValues = new HashMap<ParameterInfo, List<Object>>();

	public static class Builder implements ServiceRequestInfoBuilder {

		private final ServiceInfo serviceInfo;

		private final Map<ParameterInfo, String> pathValues = new HashMap<ParameterInfo, String>();

		private final Map<String, String> headers = new HashMap<String, String>();

		private final Map<ParameterInfo, List<Object>> queryValues = new HashMap<ParameterInfo, List<Object>>();

		private final Map<ParameterInfo, List<Object>> formValues = new HashMap<ParameterInfo, List<Object>>();

		public Builder(ServiceInfo serviceInfo) {
			this.serviceInfo = serviceInfo;
		}

		public void addHeaderParam(String name, String value) {
			this.headers.put(name, value);
		}

		public void setPathParam(String name, String value) {
			if (name == null || value == null) {
				throw new IllegalArgumentException("Name and value cannot be null");
			}
			ParameterInfo pathParam = serviceInfo.getPathParam(name);
			this.pathValues.put(pathParam, value);
		}

		public void addQueryParam(String name, Object value) {
			if (name == null || value == null) {
				throw new IllegalArgumentException("Name and value cannot be null");
			}
			ParameterInfo queryParam = serviceInfo.getQueryParam(name);
			List<Object> queryParameterValues = queryValues.get(queryParam);
			if (queryParameterValues == null) {
				queryParameterValues = new ArrayList<Object>();
			}
			queryValues.put(queryParam, queryParameterValues);
		}

		public void addFormParam(String name, Object value) {
			if (name == null || value == null) {
				throw new IllegalArgumentException("Name and value cannot be null");
			}
			ParameterInfo formParam = serviceInfo.getFormParam(name);
			List<Object> formParameterValues = formValues.get(formParam);
			if (formParameterValues == null) {
				formParameterValues = new ArrayList<Object>();
			}
			formValues.put(formParam, formParameterValues);
		}

		public ServiceRequestInfo build() {
			String serviceUrl = getServiceUrl();
			return new ServiceRequestInfoImpl(this, serviceUrl);
		}

		private String getServiceUrl() {
			String serviceUrl = serviceInfo.getServiceUrl();
			List<ParameterInfo> pathParams = serviceInfo.getPathParams();
			for (ParameterInfo param : pathParams) {
				if (pathValues.containsKey(param)) {
					serviceUrl.replaceAll("{" + param.getName() + "}", pathValues.get(param));
				} else {
					throw new IllegalStateException(String.format("Parameter '%s' without value", param.getName()));
				}
			}
			return serviceUrl;
		}
	}

	private ServiceRequestInfoImpl(Builder builder, String serviceUrl) {
		this.serviceUrl = serviceUrl;
		this.serviceInfo = builder.serviceInfo;
		this.headers.putAll(builder.headers);
		this.queryValues.putAll(builder.queryValues);
		this.formValues.putAll(builder.formValues);
	}

	public ServiceInfo getServiceInfo() {
		return serviceInfo;
	}

	public String getServiceUrl() {
		return serviceUrl;
	}

	@Override
	public Map<String, String> getHeaders() {
		return Collections.unmodifiableMap(headers);
	}

	public Map<ParameterInfo, List<Object>> getQueryValues() {
		return Collections.unmodifiableMap(queryValues);
	}

	public Map<ParameterInfo, List<Object>> getFormValues() {
		return Collections.unmodifiableMap(formValues);
	}
}
